#include <user/ulib.h>

static struct file_t files_pool[NFILES];
static struct file_t *file_free;

int
file_init(void)
{
	 int i;
	 for (i = 0; i != NFILES - 1; ++i)
	 {
		  memset(files_pool + i, 0, sizeof(struct file_t));
		  files_pool[i].free_next = files_pool + i + 1;
	 }

	 files_pool[NFILES - 1].free_next = NULL;
	 file_free = files_pool;

	 return 0;
}

struct file_t *
file_alloc(void)
{
	 struct file_t *result = file_free;

	 if (result != NULL)
	 {
		  file_free = result->free_next;
		  result->free_next = NULL;
	 }
	 
	 return result;
}

struct file_t *
file_open_root(void)
{
	 pipe_clear(rpc_in_pipe, 0);
	 pipe_clear(rpc_out_pipe, 0);
	 
	 rpc_send(get_user_service(SVC_FILE_SERVER), FSC_OPEN_ROOT);

	 struct file_t *result = file_alloc();
	 result->type = FD_NODE;
	 rpc_wait();

	 pipe_block_read(rpc_out_pipe, &result->node, sizeof(uint32_t));
	 return result;
}

struct file_t *
file_open(struct file_t * file)
{
	 if (file->type != FD_NODE)
		  return NULL;

	 pipe_clear(rpc_in_pipe, 0);
	 pipe_clear(rpc_out_pipe, 0);

	 rpc_send(get_user_service(SVC_FILE_SERVER), FSC_OPEN);
	 pipe_block_write(rpc_in_pipe, &file->node, sizeof(uint32_t));

	 uint32_t node;
	 pipe_block_read(rpc_out_pipe, &node, sizeof(uint32_t));

	 rpc_wait();

	 if (node == 0) return NULL;
	 
	 struct file_t *result = file_alloc();
	 result->type = FD_NODE;
	 result->node = node;

	 return result;
}

int
file_write(struct file_t *file, void *buf, int n)
{
	 if (file->type == FD_NODE){

		  pipe_clear(rpc_in_pipe, 0); 
		  pipe_clear(rpc_out_pipe, 0);
		  
		  rpc_send(get_user_service(SVC_FILE_SERVER), FSC_WRITE); 

		  pipe_block_write(rpc_in_pipe, &file->node, sizeof(uint32_t));
		  pipe_block_write(rpc_in_pipe, &n, sizeof(uint32_t));
		  pipe_block_write(rpc_in_pipe, buf, n); 

		  pipe_block_read(rpc_out_pipe, &n, sizeof(int)); 

		  rpc_wait();
	 }

	 return n;
}

int
file_seek(struct file_t *file, int dir, int n)
{
	 if (file->type == FD_NODE){
		  
		  pipe_clear(rpc_in_pipe, 0);
		  pipe_clear(rpc_out_pipe, 0);
		  rpc_send(get_user_service(SVC_FILE_SERVER), FSC_SEEK);

		  pipe_block_write(rpc_in_pipe, &file->node, sizeof(uint32_t));
		  pipe_block_write(rpc_in_pipe, &dir, sizeof(uint32_t));
		  pipe_block_write(rpc_in_pipe, &n, sizeof(uint32_t));

		  pipe_block_read(rpc_out_pipe, &n, sizeof(uint32_t));
		  rpc_wait();
	 }

	 return n;
}

int
file_read(struct file_t *file, void *buf, int n)
{
	 if (file->type == FD_NODE){
		  
		  pipe_clear(rpc_in_pipe, 0);
		  pipe_clear(rpc_out_pipe, 0);
		  rpc_send(get_user_service(SVC_FILE_SERVER), FSC_READ);

		  pipe_block_write(rpc_in_pipe, &file->node, sizeof(uint32_t));
		  pipe_block_write(rpc_in_pipe, &n, sizeof(int));

		  pipe_block_read(rpc_out_pipe, &n, sizeof(int));
		  pipe_block_read(rpc_out_pipe, buf, n);

		  rpc_wait();
	 }

	 return n;
}

void
file_close(struct file_t* file)
{
	 switch (file->type)
	 {
	 case FD_PIPE:
		  /* TODO */
		  break;
	 case FD_NODE:
		  /* TODO */
		  break;
	 default:
		  /* TODO */
		  break;
	 }
}
